package com.yanqu.road.entity.autodata;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.yanqu.road.entity.DataObject;
import com.yanqu.road.entity.FieldTypeAnnotation;
import com.yanqu.road.entity.autodata.chuhan.data.ChuHanUserData;
import com.yanqu.road.utils.classutil.ClassHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class AutoDataObject extends DataObject {
    private static Logger logger = LogManager.getLogger(AutoDataObject.class.getName());
    private static final Map<Class<?>, AutoUserDataClazzInfo> autoUserDataClazzInfoMap = new HashMap<>();
    private static <T> void initClazzInfo(Class<T> clazz) throws IllegalAccessException, NoSuchMethodException {
        FieldTypeAnnotation annotation = clazz.getAnnotation(FieldTypeAnnotation.class);
        if (annotation == null) {
            return;
        }
        AutoUserDataClazzInfo autoUserDataClazzInfo = new AutoUserDataClazzInfo();

        String tableName = annotation.tableName();

        Field[] fields = clazz.getDeclaredFields();
        List<Field> validFields = new ArrayList<>();
        StringBuilder updateSb = new StringBuilder();
        updateSb.append("UPDATE ").append(tableName).append(" SET ");
        List<Field> keyList = new ArrayList<>();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (!field.isAnnotationPresent(FieldTypeAnnotation.class)) {
                continue;
            }
            FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
            if (fieldTypeAnnotation.update()) {
                keyList.add(field);
            } else {
                updateSb.append(field.getName()).append("=?, ");
            }
            validFields.add(field);
        }
        updateSb.deleteCharAt(updateSb.length() - 2);//移除最后的逗号和空格
        updateSb.append(" WHERE ");
        for (int keyIdx = 0; keyIdx < keyList.size(); keyIdx++) {
            String fieldName = keyList.get(keyIdx).getName();
            if (keyIdx == 0) {
                updateSb.append(fieldName).append("=?");
            } else {
                updateSb.append(" AND ").append(fieldName).append("=?");
            }
        }
        Map<Field, List<MethodHandle>> fieldMethodMap = new HashMap<>();

        StringBuilder insertSb = new StringBuilder();
        insertSb.append("INSERT INTO ").append(tableName).append("(");
        StringBuilder valueBuilder = new StringBuilder();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
            if (fieldTypeAnnotation != null) {
                insertSb.append(field.getName()).append(",");
                valueBuilder.append("?,");

                Method getter = ClassHelper.findGetter(clazz, field);
                Method setter = ClassHelper.findSetter(clazz, field);
                // 使用 MethodHandles 调用方法
                MethodHandle getterHandle = MethodHandles.lookup().unreflect(getter);
                MethodHandle setterHandle = MethodHandles.lookup().unreflect(setter);

                List<MethodHandle> methods = new ArrayList<>();
                methods.add(getterHandle);
                methods.add(setterHandle);
                fieldMethodMap.put(field, methods);
            }
        }
        insertSb.deleteCharAt(insertSb.length() - 1);
        valueBuilder.deleteCharAt(valueBuilder.length() - 1);
        insertSb.append(") VALUES(");
        insertSb.append(valueBuilder).append(")");

        autoUserDataClazzInfo.setUpdateSql(updateSb.toString());
        autoUserDataClazzInfo.setInsertSql(insertSb.toString());
        autoUserDataClazzInfo.setFieldMethodHandleMap(fieldMethodMap);
        autoUserDataClazzInfo.setTableName(tableName);
        autoUserDataClazzInfo.setKeyList(keyList);
        autoUserDataClazzInfo.setFields(validFields);

        Constructor<T> constructor = clazz.getConstructor();
        autoUserDataClazzInfo.setConstructor(constructor);

        autoUserDataClazzInfoMap.put(clazz, autoUserDataClazzInfo);
    }

    public static AutoUserDataClazzInfo getAutoDataFieldInfo(Class<?> clazz) {
        return autoUserDataClazzInfoMap.get(clazz);
    }

    public static boolean loadAllClazzInfo() {
        try {
            Class<SqlTypeHelper> sqlTypeHelperClass = SqlTypeHelper.class;

            Package pack = AutoDataObject.class.getPackage();
            for (Class<?> clazz : ClassHelper.getClasses(pack)) {
                analysisClass(clazz);
                AutoActivityConfigObject.analysisClass(clazz);
            }
            return true;
        } catch (Exception e) {
            logger.error("load autodata failure", e);
            return false;
        }
    }

    public static void analysisClass(Class<?> clazz) throws IllegalAccessException, NoSuchMethodException {
        if (!AutoDataObject.class.isAssignableFrom(clazz)) {
            return;
        }
        FieldTypeAnnotation annotation = clazz.getAnnotation(FieldTypeAnnotation.class);
        if (annotation != null) {
            initClazzInfo(clazz);
        }
    }


    public static <T> String getUpdateSql(Class<T> clazz) {
        AutoUserDataClazzInfo autoUserDataClazzInfo = getAutoDataFieldInfo(clazz);
        if (autoUserDataClazzInfo == null) {
            return "";
        }
        return autoUserDataClazzInfo.getUpdateSql();
    }

    public static <T> String getInsertSql(Class<T> clazz) {
        AutoUserDataClazzInfo autoUserDataClazzInfo = getAutoDataFieldInfo(clazz);
        if (autoUserDataClazzInfo == null) {
            return "";
        }
        return autoUserDataClazzInfo.getInsertSql();
    }

    public <T> String getUpdateSql() {
        AutoUserDataClazzInfo autoUserDataClazzInfo = getAutoDataFieldInfo(this.getClass());
        if (autoUserDataClazzInfo == null) {
            return "";
        }
        return autoUserDataClazzInfo.getUpdateSql();
    }

    public <T> String getInsertSql() {
        AutoUserDataClazzInfo autoUserDataClazzInfo = getAutoDataFieldInfo(this.getClass());
        if (autoUserDataClazzInfo == null) {
            return "";
        }
        return autoUserDataClazzInfo.getInsertSql();
    }

    public static <T> String parseUpdateSql(Class<T> clazz) {
        FieldTypeAnnotation annotation = clazz.getAnnotation(FieldTypeAnnotation.class);
        if (annotation == null) {
            return null;
        }

        String tableName = annotation.tableName();

        Field[] fields = clazz.getDeclaredFields();
        StringBuilder updateSb = new StringBuilder();
        updateSb.append("UPDATE ").append(tableName).append(" SET ");
        List<String> keyList = new ArrayList<>();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (!field.isAnnotationPresent(FieldTypeAnnotation.class)) {
                continue;
            }
            FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
            if (fieldTypeAnnotation.update()) {
                keyList.add(field.getName());
            }
            if (i < fields.length - 1) {
                updateSb.append(field.getName()).append("=?, ");
            } else {
                updateSb.append(field.getName()).append("=?");
            }
        }
        updateSb.append(" WHERE ");
        for (int keyIdx = 0; keyIdx < keyList.size(); keyIdx++) {
            String fieldName = keyList.get(keyIdx);
            if (keyIdx == 0) {
                updateSb.append(fieldName).append("=?");
            } else {
                updateSb.append(" AND ").append(fieldName).append("=?");
            }
        }

        return updateSb.toString();
    }

    public static <T> String parseInsertSql(Class<T> clazz) {
        FieldTypeAnnotation annotation = clazz.getAnnotation(FieldTypeAnnotation.class);
        if (annotation == null) {
            return null;
        }
        String tableName = annotation.tableName();
        Field[] fields = clazz.getDeclaredFields();
        StringBuilder insertSb = new StringBuilder();
        insertSb.append("INSERT INTO ").append(tableName).append("(");
        StringBuilder valueBuilder = new StringBuilder();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (!field.isAnnotationPresent(FieldTypeAnnotation.class)) {
                continue;
            }
            FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
            if (fieldTypeAnnotation != null) {
                insertSb.append(field.getName()).append(",");
                valueBuilder.append("?,");
            }

        }
        insertSb.deleteCharAt(insertSb.length() - 1);
        valueBuilder.deleteCharAt(valueBuilder.length() - 1);
        insertSb.append(") VALUES(");
        insertSb.append(valueBuilder).append(")");

        return insertSb.toString();
    }

    public static <T> String parseCreateTableSql(Class<T> clz) {
        FieldTypeAnnotation annotation = clz.getAnnotation(FieldTypeAnnotation.class);
        if (annotation == null) {
            return null;
        }
        Field[] fields = clz.getDeclaredFields();
        String tableName = "`" + clz.getAnnotation(FieldTypeAnnotation.class).tableName() + "`";
        String keyUnique = "";
        List<String> keyList1 = new ArrayList<>();
        String fildStr = "";
        for (Field field : fields) {
            if (field.isAnnotationPresent(FieldTypeAnnotation.class)) {
                FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
                String fieldName = "`" + field.getName() + "`";
                String fieldDesc = "'" + fieldTypeAnnotation.desc() + "'";
                if (fieldTypeAnnotation.update()) {
                    keyList1.add(fieldName);
                    if (keyUnique.length() == 0) {
                        keyUnique += "`" + field.getName();
                    } else if (keyUnique.length() > 0) {
                        keyUnique += "_" + field.getName();
                    }
                }

                if (field.getType() == int.class || field.getType() == boolean.class) {
                    fildStr += "  " + fieldName + " int(11) NOT NULL COMMENT " + fieldDesc + ",\n";
                } else if (field.getType() == long.class) {
                    fildStr += "  " + fieldName + " bigint(20) NOT NULL COMMENT " + fieldDesc + ",\n";
                } else if (field.getType() == Date.class) {
                    fildStr += "  " + fieldName + " datetime NOT NULL COMMENT " + fieldDesc + ",\n";
                } else if (field.getType() == BigInteger.class || field.getType() == BigDecimal.class) {
                    fildStr += "  " + fieldName + " varchar(255) NOT NULL COMMENT " + fieldDesc + ", \n";
                } else if (field.getType() == String.class ||
                        field.getType() == List.class || field.getType() == Map.class) {
                    fildStr += "  " + fieldName + " text NOT NULL COMMENT " + fieldDesc + ", \n";
                } else {
                    fildStr += "  " + fieldName + " text NOT NULL COMMENT " + fieldDesc + ", \n";
                }
            }
        }
        keyUnique += "`";
        String sql = "CREATE TABLE " + tableName + " (\n" +
                "  `dbId` bigint(20) NOT NULL AUTO_INCREMENT,\n" +
                fildStr +
                "  PRIMARY KEY (`dbId`),\n" +
                "  UNIQUE KEY " + keyUnique + " (" + StringUtils.listToString(keyList1, ',') + ") USING BTREE\n" +
                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;";
        return sql;
    }
}
